package cc.rengu.igas.bsps.core.service.trans;

import cc.rengu.igas.bsps.common.constant.BspsAppParamConstant;
import cc.rengu.igas.bsps.common.enums.RespCodeEnum;
import cc.rengu.igas.bsps.core.model.UserSessionInfo;
import cc.rengu.igas.bsps.core.service.base.ManageService;
import cc.rengu.igas.bsps.facade.base.Header;
import cc.rengu.igas.bsps.facade.bean.AesKeyInfoBean;
import cc.rengu.igas.bsps.facade.request.SyncAesKeyRequest;
import cc.rengu.igas.bsps.facade.response.SyncAesKeyResponse;
import cc.rengu.oltp.service.common.constant.AppParamConstant;
import cc.rengu.oltp.service.common.dao.CertInfoMapper;
import cc.rengu.oltp.service.common.dao.SecPlanInfoMapper;
import cc.rengu.oltp.service.common.dao.SrcChannelInfoMapper;
import cc.rengu.oltp.service.common.dao.impl.CertInfoMapperImpl;
import cc.rengu.oltp.service.common.dao.impl.SecPlanInfoMapperImpl;
import cc.rengu.oltp.service.common.dao.impl.SrcChannelInfoMapperImpl;
import cc.rengu.oltp.service.common.entity.CertInfo;
import cc.rengu.oltp.service.common.entity.SecPlanInfo;
import cc.rengu.oltp.service.common.entity.SrcChannelInfo;
import cc.rengu.oltp.service.common.enums.OltpRpcdEnum;
import cc.rengu.oltp.service.model.BizException;
import cc.rengu.oltp.service.model.BizResponse;
import cc.rengu.oltp.utility.util.ConvertUtil;
import cc.rengu.oltp.utility.util.RSAUtil;
import cc.rengu.oltp.utility.util.RandomUtil;
import cc.rengu.oltp.utility.util.RedisUtil;
import com.alibaba.fastjson.JSON;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
import org.springframework.util.CollectionUtils;

import javax.crypto.Cipher;
import java.io.File;
import java.io.IOException;
import java.security.interfaces.RSAPrivateKey;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 同步AES密钥.
 */
public class SyncAesKeyService extends ManageService {

    /**
     * 会话密钥
     */
    private static String SIGNATURE_KEY_TYPE = "00";
    /**
     * 敏感信息密钥
     */
    private static String SENSITIVE_KEY_TYPE = "01";

    /**
     * 本地文件
     */
    private static String FILE_TYPE = "2";

    /**
     * 数据库存储
     */
    private static String DATA_TYPE = "0";

    @Override
    protected Object bizBeforeCust() throws Exception {
        SyncAesKeyRequest syncAesKeyRequest = new SyncAesKeyRequest();
        ConvertUtil.convertOutput(syncAesKeyRequest);
        Header header = syncAesKeyRequest.getHeader();
        if (CollectionUtils.isEmpty(syncAesKeyRequest.getAesKeyInfoBeanList())) {
            rglog.error("密钥信息为空！instId:<{}>,TraceNo:<{}>", header.getInstId(),
                    header.getTraceNo());
            throw new BizException(RespCodeEnum.MSG_VALID_CHECK_ERROR.getRespCode(), RespCodeEnum.MSG_VALID_CHECK_ERROR.getRespCode());
        }

        List<String> keyList = syncAesKeyRequest.getAesKeyInfoBeanList().stream().map(AesKeyInfoBean::getKeyType).collect(Collectors.toList());

        if (CollectionUtils.isEmpty(keyList)
                || keyList.size() != 2) {
            rglog.error("密钥类型非法或为空！instId:<{}>,TraceNo:<{}>", header.getInstId(),
                    header.getTraceNo());
            throw new BizException(RespCodeEnum.MSG_VALID_CHECK_ERROR.getRespCode(), RespCodeEnum.MSG_VALID_CHECK_ERROR.getRespCode());
        }

        if (!(keyList.contains(SIGNATURE_KEY_TYPE)
                && keyList.contains(SENSITIVE_KEY_TYPE))) {
            rglog.error("会话与敏感信息密钥必须上送！instId:<{}>,TraceNo:<{}>", header.getInstId(),
                    header.getTraceNo());
            throw new BizException(RespCodeEnum.MSG_VALID_CHECK_ERROR.getRespCode(), RespCodeEnum.MSG_VALID_CHECK_ERROR.getRespCode());
        }

        return syncAesKeyRequest;
    }

    @Override
    protected BizResponse bizProcessCust(Object request) throws Exception {

        SyncAesKeyRequest syncAesKeyRequest = (SyncAesKeyRequest) request;
        Header header = syncAesKeyRequest.getHeader();
        if (CollectionUtils.isEmpty(syncAesKeyRequest.getAesKeyInfoBeanList())) {
            rglog.error("密钥信息为空！instId:<{}>,TraceNo:<{}>", header.getInstId(),
                    header.getTraceNo());
            throw new BizException(RespCodeEnum.MSG_VALID_CHECK_ERROR.getRespCode(), RespCodeEnum.MSG_VALID_CHECK_ERROR.getRespCode());
        }

        //通过Redis获取交易渠道信息
        String srcChannelInfoKey = header.getInstId() + header.getSrcSysId() + header.getChanlId().trim();
        SrcChannelInfo srcChannelInfo = JSON.parseObject(RedisUtil.hashGet(AppParamConstant.CHANNEL_ACQ_INFO_CACHE, srcChannelInfoKey), SrcChannelInfo.class);
        if (null == srcChannelInfo) {
            srcChannelInfoKey = header.getInstId().trim() + header.getSrcSysId().trim() + "*";
            srcChannelInfo = JSON.parseObject(RedisUtil.hashGet(AppParamConstant.CHANNEL_ACQ_INFO_CACHE, srcChannelInfoKey), SrcChannelInfo.class);
            if (null == srcChannelInfo) {
                SrcChannelInfoMapper srcChannelInfoMapper = new SrcChannelInfoMapperImpl();
                srcChannelInfo = srcChannelInfoMapper.selectSrcChannelInfoByPrimaryKey(txnInfo.getSrcTxnAuthCfg().getInstId().trim(), txnInfo.getSrcTxnAuthCfg().getMsgSrcId().trim(), txnInfo.getSrcTxnAuthCfg().getTransChannelId().trim());
            }
        }
        if (null == srcChannelInfo) {
            rglog.error("交易渠道<{}>的细分渠道<{}>未配置或已停用！", txnInfo.getSrcTxnAuthCfg().getMsgSrcId(), txnInfo.getSrcTxnAuthCfg().getTransChannelId());
            throw new BizException(OltpRpcdEnum.CHANNEL_DISABLED);
        }

        //安全标识
        String secPlanId = srcChannelInfo.getSecPlanId();
        //获取机构私钥证书
        SecPlanInfoMapper secPlanInfoMapper = new SecPlanInfoMapperImpl();
        SecPlanInfo secPlanInfo = secPlanInfoMapper.selectSecPlanInfoByPrimaryKey(header.getInstId(), secPlanId, BspsAppParamConstant.PRIVATE_DECRYPT_CERT_TYPE);
        if (null == secPlanInfo) {
            rglog.error("安全计划查询为空！instId:<{}>,secPlanId:<{}>,secPlanType:<{}>", header.getInstId(),
                    secPlanId, BspsAppParamConstant.PRIVATE_DECRYPT_CERT_TYPE);
            throw new BizException(RespCodeEnum.RECORD_NOT_FOUND.getRespCode(), "机构安全计划查询为空");
        } else if (!AppParamConstant.YES.equals(secPlanInfo.getSecPlanStatus())) {
            rglog.error("安全计划状态已失效！instId:<{}>,secPlanId:<{}>,secPlanType:<{}>,状态:<{}>", header.getInstId(),
                    secPlanId, BspsAppParamConstant.PRIVATE_DECRYPT_CERT_TYPE, secPlanInfo.getSecPlanStatus());
            throw new BizException(RespCodeEnum.RECORD_NOT_FOUND.getRespCode(), "机构安全计划状态失效");
        }

        //查询证书表 获取机构私钥
        CertInfoMapper certInfoMapper = new CertInfoMapperImpl();
        String certIndex = secPlanInfo.getInstId() + secPlanInfo.getSecPlanId() + secPlanInfo.getSecPlanType();
        CertInfo certInfo = certInfoMapper.selectCertInfoByPrimaryKey(certIndex);
        if (null == certInfo) {
            rglog.error("机构私钥证书查询为空！certIndex:<{}>", certIndex);
            throw new BizException(RespCodeEnum.RECORD_NOT_FOUND.getRespCode(), "机构私钥证书查询为空");
        } else if (!AppParamConstant.YES.equals(certInfo.getKeyStatus())) {
            rglog.error("机构私钥证书查询证书状态失效！certIndex:<{}>", certIndex);
            throw new BizException(RespCodeEnum.QUERY_STATUS_ERROR.getRespCode(), "机构私钥证书状态已失效");
        }

        String privateKey;
        if (FILE_TYPE.equals(certInfo.getCertSaveType())) {
            rglog.info("证书以文件的方式存放，certIndex:<{}>,certSavePath:<{}>", certIndex, certInfo.getCertSavePath());
            try {
                privateKey = FileUtils.readFileToString(new File(certInfo.getCertSavePath()));
            } catch (IOException e) {
                rglog.error("读取证书文件异常:{}", e.getMessage());
                throw new BizException(RespCodeEnum.SYSTEM_ERROR.getRespCode(), RespCodeEnum.SYSTEM_ERROR.getRespDesc());
            }
        } else if (DATA_TYPE.equals(certInfo.getCertSaveType())) {
            privateKey = certInfo.getCertValue();
        } else {
            rglog.error("暂不支持处理除本地数据存储或本地文件外的证书");
            throw new BizException(RespCodeEnum.SYSTEM_ERROR.getRespCode(), RespCodeEnum.SYSTEM_ERROR.getRespDesc());
        }

        UserSessionInfo userSessionInfo = new UserSessionInfo();
        //解密会话密钥与敏感信息密钥
        for (AesKeyInfoBean aesKeyInfoBean : syncAesKeyRequest.getAesKeyInfoBeanList()) {
            String key = privateKeyDecrypt(Base64.decodeBase64(aesKeyInfoBean.getKeyValue()), RSAUtil.getPrivateKeyFromBase64String(privateKey));
            if (null == key) {
                rglog.error("解密AES密钥失败，拒绝交易");
                throw new BizException(RespCodeEnum.DECRYP_SECRET_ERROR.getRespCode(), RespCodeEnum.DECRYP_SECRET_ERROR.getRespDesc());
            }

            if (SIGNATURE_KEY_TYPE.equals(aesKeyInfoBean.getKeyType())) {
                //会话密钥
                userSessionInfo.setSignatureKey(key);
            } else if (SENSITIVE_KEY_TYPE.equals(aesKeyInfoBean.getKeyType())) {
                //敏感信息加密密钥
                userSessionInfo.setSensitiveKey(key);
            }
        }

        //生成sessionId 存入redis缓存
        String sessionId = RandomUtil.getUUID();
        header.setSession(sessionId);
        RedisUtil.onceSet(sessionId, JSON.toJSONString(userSessionInfo), BspsAppParamConstant.SESSION_EFFECTIVE_TIME);

        BizResponse<SyncAesKeyResponse> bizResponse = new BizResponse<>();
        SyncAesKeyResponse syncAesKeyResponse = new SyncAesKeyResponse();
        ConvertUtil.convertOutput(syncAesKeyResponse);
        syncAesKeyResponse.setHeader(header);
        bizResponse.setResult(syncAesKeyResponse);
        bizResponse.setRspSysId(AppParamConstant.SYS_ID);
        bizResponse.setRespCode(RespCodeEnum.TRANS_SUCCESS.getRespCode());
        bizResponse.setRespDesc(RespCodeEnum.TRANS_SUCCESS.getRespDesc());
        return bizResponse;
    }

    @Override
    protected void bizAfterCust(Object response) throws Exception {
        BizResponse bizResponse = (BizResponse) response;
        SyncAesKeyResponse syncAesKeyResponse = (SyncAesKeyResponse) bizResponse.getResult();
        ConvertUtil.convertInput(syncAesKeyResponse);
    }

    /**
     * 使用私钥解密数字信封，得到AES秘钥明文
     *
     * @param decryptDta 数字信封
     * @param privateKey RSA私钥
     * @return 秘钥明文
     * @throws Exception
     */
    private String privateKeyDecrypt(byte[] decryptDta, RSAPrivateKey privateKey) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        cipher.doFinal(decryptDta);
        return new String(cipher.doFinal(decryptDta));
    }

}
